/*	--- 观察者 --- 

# 定义对象间的一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖于它的对象都得到通知并被自动更新。

=========================================================

【观察者模式】（Observer）又称【发布-订阅模式】（Publish-Subscribe：Pub/Sub）。

它是一种【通知机制】，让【发送通知的一方】（被观察方）和【接收通知的一方】（观察者）能【彼此分离】，互不影响。

要理解【观察者模式】，我们还是看例子。

------------

假设一个【电商网站】，有【多种Product（商品）】，同时，【Customer（消费者）和Admin（管理员）】对【商品上架、价格改变】都感兴趣，希望能第一时间获得【通知】。


于是，【Store】（商场）可以这么写： */
public class Store {
	Customer customer;
	Admin admin;

	private Map<String, Product> products = new HashMap<>();

	public void addNewProduct(String name, double price) {
		Product p = new Product(name, price);
		products.put(p.getName(), p);

		// 通知用户
		customer.upPublished(p);

		// 通知管理员
		admin.upPublished(p);
	}

	public void setProductPrice(String name, double price) {
		Product p = products.get(name);
		p.setPrice(price);

		// 通知用户
		customer.onPriceChanged(p); // onPriceChanged()通知函数

		// 通知管理员
		admin.onPriceChanged(p); // onPriceChanged()通知函数
	}
}

class Product {
	String name;
	double price;

	Product(String name, double price) {
		this.name = name;
		this.price = price;
	}
}

/*
我们观察上述Store类的问题：它直接引用了【Customer和Admin】。

先不考虑【多个Customer/多个Admin】的问题，【上述Store类】最大的问题是，如果要加一个【新的观察者类型】，例如【工商局管理员，Store类】就必须继续改动。

因此，上述问题的本质是【Store】希望【发送通知】给那些【关心Product的对象】，但【Store】并不想知道【这些人】是谁。

【观察者模式】就是要分离【被观察者】和【观察者】之间的【耦合关系】。

要实现这一目标也很简单，【Store】不能直接引用/存储【变量Customer和Admin】，

相反，它引用一个【ProductObserver接口】，任何人想要【观察Store】，只要实现【该接口】，并且把自己【注册到Store】即可

重构后,
【被观测者 Store】 : */
public class Store {
// Observer模式
	private List<ProductObserver> observers = new ArrayList<>();
	private Map<String, Product> products = new HashMap<>();

	// 注册【观察者】
	public void addObserver(ProductObserver observer) {
		this.observers.add(observer);
	}

	// 取消注册
	public void removeObserver(ProductObserver observer) {
		this.observers.remove(observer);
	}

// 实际提供【功能】
	public void addNewProduct(String name, double price) {
		Product p = new Product(name, price);
		products.put(p.getName(), p);

		// 通知【观察者】们
		observers.forEach(o -> o.onPublished(p));
	}

	public void setProductPrice(String name, double price) {
		Product p = products.get(name); // 获取已有Product
		p.setPrice(price); // 设置价格

		// 通知【观察者】们
		observers.forEach(o -> o.onPriceChanged(p));
	}
}


/*
就是这么一个【小小的改动】，使得【观察者类型】就可以【无限扩充】，而且，【观察者的定义】可以放到【客户端】： */

// observer:
Admin a = new Admin();
Customer c = new Customer();

// store
Store store = new Store();

// 注册【观察者】
store.addObserver(a); 
store.addObserver(c);
	/* class Store implements ProductObservable
		interface ProductObservable
			addObserver
			removeObserver
*/

/*
甚至可以注册【匿名观察者】： */
store.addObserver(new ProductObserver() {
	public void onPublished(Product product) {
		System.out.println("[Log] on product published: " + product);
	}

	public void onPriceChanged(Product product) {
		System.out.println("[Log] on product price changed: " + product);
	}
});


/*
用一张图画出【观察者模式】：

	┌─────────┐ imp  ┌───────────────┐
	│  Store  │─ ─ ─>│ProductObserver│
	└─────────┘      └───────────────┘
	     │                   ▲
	                         │
	     │             ┌─────┴─────┐
	     ▼             │           │
	┌─────────┐   ┌─────────┐ ┌─────────┐
	│ Product │   │  Admin  │ │Customer │ ...
	└─────────┘   └─────────┘ └─────────┘


【观察者模式】也有【很多变体形式】。

【有的观察者模式】把【被观察者】也【抽象出接口】： */
public interface ProductObservable {
// 注意此处拼写是Observable不是Observer!
	void addObserver(ProductObserver observer);
	void removeObserver(ProductObserver observer);
}


/*
对应的【实体被观察者】就要实现【该接口】： */
public class Store implements ProductObservable {
	//...
}

/*
有些【观察者模式】把【 通知】变成【一个Event对象】，从而不再有【多种方法通知】，而是统一成【一种通知】： */
public interface ProductObserver {
	void onEvent(ProductEvent event);
}


/*
让【观察者】自己从【Event对象】中读取【通知类型】和【通知数据】。

【广义的观察者模式】包括【所有消息系统】。

所谓【消息系统】，就是把【观察者】和【被观察者】完全分离，通过【消息系统】本身来【通知】：

                 ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
                   Messaging System
                 │                       │
                   ┌──────────────────┐
              ┌──┼>│Topic:newProduct  │──┼─┐    ┌─────────┐
              │    └──────────────────┘    ├───>│ConsumerA│
┌─────────┐   │  │ ┌──────────────────┐  │ │    └─────────┘
│Producer │───┼───>│Topic:priceChanged│────┘
└─────────┘   │  │ └──────────────────┘  │
              │    ┌──────────────────┐         ┌─────────┐
              └──┼>│Topic:soldOut     │──┼─────>│ConsumerB│
                   └──────────────────┘         └─────────┘
                 └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘


【消息发送方】称为【Producer】，【消息接收方】称为【Consumer】，

【Producer发送消息】的时候，必须选择【发送】到【哪个Topic】。

【Consumer】可以【订阅】自己【感兴趣的Topic】，从而【只获得特定类型的消息】。

使用【消息系统】实现【观察者模式】时，【Producer和Consumer】甚至经常不在【同一台机器】上，并且【双方】对【对方】完全【一无所知】，因为【注册观察者】这个动作本身都在【消息系统】中完成，而不是在【Producer内部】完成。

此外，注意到我们在【编写观察者模式】的时候，【通知Observer】是【依靠语句】： */
observers.forEach(o -> o.onPublished(p));


/*
这说明【各个观察者】是【依次获得】的【同步通知】，如果【上一个观察者】处理太慢，会导致【下一个观察者】不能及时【获得通知】。

此外，如果【观察者】在【处理通知的时候】，发生了【异常】，还需要【被观察者】处理异常，才能保证继续通知【下一个观察者】。

---------------------------

思考：如何改成【异步通知】，使得【所有观察者】可以【并发】同时处理？

有的童鞋可能发现【Java标准库】有个【java.util.Observable类】和【一个Observer接口】，用来帮助我们实现【观察者模式】。

但是，这个类【非常不！好！用！】

实现【观察者模式】的时候，也【不推荐】借助【这两个东东】。


=================================================

#	练习	

给【Store】增加【一种类型的观察者】，并把【通知】改为【异步】。 */

class PriceManagement implements ProductObserver {
	@Override
	public void onPublished(Product product) {
		System.out.println("[PriceManagement] on product published: " + product);
	}

	@Override
	public void onPriceChanged(Product product) {
		System.out.println("[PriceManagement] on product price changed: " + product);
	}
}

public void addNewProduct(String name, double price) {
	Product p = new Product(name, price);
	products.put(p.getName(), p);

	observers.forEach(o -> new Thread() {
		@Override
		public void run() {
			o.onPublished(p);
		}
	}.start());
}

public void setProductPrice(String name, double price) {
	Product p = products.get(name);
	p.setPrice(price);

	observers.forEach(o -> new Thread() {
		public void run() {
			o.onPriceChanged(p);
		}
	}.start());
}


/*==================================================


#	----- 观察者 の 小结 -----


1. 【观察者模式】，又称【发布-订阅模式】，是一种【一对多的通知机制】，使得双方无需关心对方，只关心【通知本身】。





*/





































































